package com.xuanyue.db.xuan.antlr.v2.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

import com.xuanyue.db.xuan.IResultV2;
import com.xuanyue.db.xuan.SeachContextV2;
import com.xuanyue.db.xuan.antlr.v2.BitQV2Parser.QueryContext;
import com.xuanyue.db.xuan.core.db.ColumnMetaV2;
import com.xuanyue.db.xuan.core.db.TableMetaV2;
import com.xuanyue.db.xuan.core.table.IBitIndex;
import com.xuanyue.db.xuan.core.table.IColumn;
import com.xuanyue.db.xuan.core.table.ISortElement;
import com.xuanyue.db.xuan.core.table.expr.Expr;
import com.xuanyue.db.xuan.core.table.expr.ExprValue;
import com.xuanyue.db.xuan.core.table.expr.ExprValueType;
import com.xuanyue.db.xuan.core.table.expr.Function;
import com.xuanyue.db.xuan.core.table.expr.IFunctionExecutor;
import com.xuanyue.db.xuan.core.table.expr.LimitInfo;
import com.xuanyue.db.xuan.core.table.expr.Settor;
import com.xuanyue.db.xuan.core.table.expr.SortTemplate;
import com.xuanyue.db.xuan.core.table.sort.LimitHandlerV2;

/**
 * SQL执行器
 *
 * @author 解观海
 * @email guanhaixie@sina.cn
 * @date 2021年1月9日
 *
 */
public class BitQV2ListenerImpl extends AbstractBitQV2Listener implements IResultV2{

	
	private List<IBitIndex> caches;
	private int now;
	private int num;
	private int count=-1;
	//private List<Map<String,ExprValue>> result;
	private ExprValue result = new ExprValue();
	private long versionNow;
	
	
	public BitQV2ListenerImpl(int num) {
		this.num=num;
	}
	
	@Override
	public ExprValue getResult() {
		return this.result;
	}
	
	protected void exeCopyMask() {
		caches.get(now).copyFrom( table.getMask() );
	}
	
	@Override
	protected void exeOr(int offset) {
		caches.get(now).or(caches.get(now+offset));
	}

	@Override
	protected void exeAnd(int offset) {
		caches.get(now).and(caches.get(now+offset));
	}

	@Override
	protected void exeSetAll(boolean value) {
		caches.get(now).setAll(value);
	}

	@Override
	protected void exeAndMask() {
		caches.get(now).and(  table.getMask() );
	}

	@Override
	protected void handle(boolean notIsNull) {
		if(!notIsNull) {
			caches.get(now).not();
		}
	}

	@Override
	protected int handle(Expr expr) {
		ExprValue ev = expr.getValue();
		if(ExprValueType.TRANSATTR.is(ev.getType())) {
			Map<String,ExprValue> ps = this.getSingleParameter().getValue();
			String key = ev.getValue();
			expr.setValue( ps.get(key) );
		}
		
		this.table.expr(expr, caches.subList(now, caches.size()));
		expr.setValue(ev);//保持参数的不变性
		return 1;
	}

	@Override
	protected int handle(Function function) {
		IFunctionExecutor<? extends IColumn> iFunction = SeachContextV2.getFunction(function.getFunctionName());
		iFunction.exprExe(table.getColumn(function.getCname()), new ExprValue(ExprValueType.ARRAY,function.getValues()), caches.subList(now, caches.size()));
		return 1;
	}

	@Override
	protected void handle(List<SortTemplate> sortTemplateList, LimitInfo limitInfo) {
		List<Integer> ids = null;
		List<ISortElement> sortElementList = null;
		int total = caches.get(now).cardinality();
		if(sortTemplateList!=null) {
			sortElementList = new ArrayList<>();
			for(SortTemplate st:sortTemplateList) {
				sortElementList.addAll( table.getSortE(st) );
			}
			LimitHandlerV2 h2 = new LimitHandlerV2(limitInfo.getOffset(), limitInfo.getNum(), sortElementList, 
					caches.get(now), caches.get(now+1), caches.get(now+2), caches.get(now+3));
			ids = h2.limit(0.2f);
		}else {
			LimitHandlerV2 h2 = new LimitHandlerV2(limitInfo.getOffset(), limitInfo.getNum(),caches.get(now));
			ids = h2.limit(0.2f);
		}
		List<Map<String, ExprValue>> resultList = new ArrayList<>();
		Map<String, ExprValue> one = null;
		for(int rowId:ids) {
			one = table.readByIdReserve(rowId,fields,sortElementList,versionNow);
			if(one!=null) {
				resultList.add(one);
			}else {
				total--;
			}
		}
		if(sortTemplateList!=null) {
			resultList.sort( new SortComparator());
		}
		if(resultList.size()>limitInfo.getNum()) {
			resultList=resultList.subList(0, limitInfo.getNum());
		}
		Map<String,ExprValue> res = new HashMap<>();
		res.put("count", new ExprValue(ExprValueType.INT,total));
		
		List<ExprValue> rl = new ArrayList<>();
		for(Map<String, ExprValue> o:resultList) {
			rl.add(new ExprValue(ExprValueType.MAP,o));
		}
		res.put("rs", new ExprValue(ExprValueType.ARRAY,rl));
		result.setType(ExprValueType.MAP);
		result.setValue(res);
	}

	@Override
	protected void exeUpdate(List<Settor> settors) {
		Map<String,ExprValue> vs = new HashMap<>();
		for(Settor settor:settors) {
			vs.put(settor.getCname(),settor.getExprValue());
		}
		IBitIndex ids = caches.get(now);
		count = 0;
		for (int j = ids.nextSetBit(0); j >= 0; j = ids.nextSetBit(j+1)) {
			count+=table.updateByRowid(vs, j,versionNow);
		}
		result.setType(ExprValueType.INT);
		result.setValue(count);
	}

	@Override
	protected void exeDelete() {
		IBitIndex ids = caches.get(now);
		this.count = 0;
		for (int j = ids.nextSetBit(0); j >= 0; j = ids.nextSetBit(j+1)) {
			count+=table.deleteByRowid(j,versionNow);
		}
		result.setType(ExprValueType.INT);
		result.setValue(count);
	}
	
	@Override
	protected void exeInsertInto(Map<String, ExprValue> c2Value) {
		Map<String, ExprValue> c2v = new HashMap<>();
		count = 0;
		if(isBatch()) {
			List<ExprValue> vsL = getBatchParameter();
			List<Map<String, ExprValue>> batch = new ArrayList<>();
			Map<String, ExprValue> c2p;// = new HashMap<>();
			for(int i=0,s=vsL.size();i<s;i++) {
				c2v = new HashMap<>();
				c2p=vsL.get(i).getValue();
				for(Entry<String,ExprValue> en:c2Value.entrySet()) {
					if(ExprValueType.TRANSATTR.is(en.getValue().getType() )) {
						c2v.put(en.getKey(), c2p.get(en.getKey()));
					}else {
						c2v.put(en.getKey(), en.getValue());
					}
				}
				batch.add(c2v);
				
			}
			List<ExprValue> ids = table.insertIntoBatch(batch);
			count=ids.size();
			
			result.setType(ExprValueType.MAP);
			Map<String,ExprValue> r = new HashMap<>();
			r.put("count", new ExprValue(ExprValueType.INT,count));
			r.put("rowids", new ExprValue(ExprValueType.ARRAY,ids));
			result.setValue(r );
		}else if(hasParameter()){
			ExprValue vs=getSingleParameter();
			Map<String, ExprValue> c2p=vs.getValue();
			for(Entry<String,ExprValue> en:c2Value.entrySet()) {
				if(ExprValueType.TRANSATTR.is(en.getValue().getType() )) {
					c2v.put(en.getKey(), c2p.get(en.getKey()));
				}else {
					c2v.put(en.getKey(), en.getValue());
				}
			}
			int rowId = table.insertInto(c2v);
			count++;
			result.setType(ExprValueType.MAP);
			Map<String,ExprValue> r = new HashMap<>();
			r.put("count", new ExprValue(ExprValueType.INT,count));
			r.put("rowid", new ExprValue(ExprValueType.INT,rowId));
			result.setValue(r);
		}else {
			//无参数
			int rowId = table.insertInto(c2Value);
			count++;
			result.setType(ExprValueType.MAP);
			Map<String,ExprValue> r = new HashMap<>();
			r.put("count", new ExprValue(ExprValueType.INT,count));
			r.put("rowid", new ExprValue(ExprValueType.INT,rowId));
			result.setValue(r);
		}
	}

	@Override
	protected void exeCreateTable(TableMetaV2 tableMetaV2) {
		SeachContextV2.createTable(tableMetaV2);
		result.setType(ExprValueType.STRING);
		result.setValue("SUCCESS");
	}

	@Override
	protected void exeSetSource(int source) {
		table.setSource(source);
		result.setType(ExprValueType.STRING);
		result.setValue("SUCCESS");
	}

	@Override
	protected void exeDropColumn(String cname) {
		SeachContextV2.dropColumn(table.getName(), cname);
		result.setType(ExprValueType.STRING);
		result.setValue("SUCCESS");
	}

	@Override
	protected void exeAddColumn(ColumnMetaV2 cmV2) {
		SeachContextV2.addColumn(table.getName(), cmV2);
		result.setType(ExprValueType.STRING);
		result.setValue("SUCCESS");
	}

	@Override
	protected void exeAlertColumn(String oldName,ColumnMetaV2 cmV2) {
		SeachContextV2.alertColumn(table.getName(), oldName, cmV2);
		result.setType(ExprValueType.STRING);
		result.setValue("SUCCESS");
	}


	@Override
	protected void handle(QueryContext query) {
		this.now = 0;
		super.handle(query);
	}


	@Override
	protected void exeApplySrouce() {
		caches=this.table.apply(num);
		this.now = 0;
	}


	@Override
	protected void exeReturnSrouce() {
		this.table.returnSource(caches);
	}

	@Override
	protected void exePP() {
		now++;
	}
	@Override
	protected void exeReduce() {
		now--;
	}

	@Override
	protected void exeVersionRemember() {
		this.versionNow = table.getCurrentVersion();
	}

	@Override
	protected ReadLock getReadLockOfMeta() {
		return SeachContextV2.getReadLockOfMeta();
	}

	@Override
	protected WriteLock getWriteLockOfMeta() {
		return SeachContextV2.getWriteLockOfMeta();
	}
	
	
}
